1-2. 외부제어·우회 계열

관통 질문: 외부 값이 시스템 동작이나 보안 판단을 직접 결정하고 있는가? 삽입 계열이 “질의의 구조”를 노린다면, 이 계열은 “판단 그 자체”를 노린다. 클라이언트나 명령줄에서 온 값을 그대로 신뢰해 권한·설정·실행 대상을 정하면, 공격자는 그 값만 바꿔서 프로그램의 판단을 조작한다.


8. 보호 메커니즘을 우회할 수 있는 입력값 변조 CWE-807

신뢰 경계: 쿠키(role 필드) → 검증 없이 → 관리자 권한 판단 ⚠️ 클라이언트에서 받는 것은 “누구인지 가리키는 표(세션 ID)“까지다. “무엇을 할 수 있는지(권한)“는 서버가 가진 값으로 판단하라.

공격 시나리오: 권한 정보를 쿠키에서 그대로 읽어 판단하면, 공격자가 프록시로 role 값을 admin 으로 바꾸는 것만으로 관리자 기능을 실행할 수 있다.

❌ Bad

char role[32];
cgiFormString("role", role, sizeof(role));
if (strcmp(role, "admin") == 0)   /* 조작 가능한 값으로 보안 결정 */
    doAdminJob();

✅ Good

char sid[64];
cgiFormString("SESSIONID", sid, sizeof(sid));       /* 세션 식별자만 받는다 */
Session *s = lookupSession(sid);
if (s == NULL || isExpired(s)) { denyAccess(); return; }
if (s->role == ROLE_ADMIN)                          /* 판단 근거는 서버 안에 있다 */
    doAdminJob();

치환 규칙: 클라이언트 값으로 직접 보안 판단 금지 → 세션 식별자만 받고 권한은 서버 저장소에서 조회.


9. 시스템 또는 구성 설정의 외부 제어 CWE-15

신뢰 경계: 명령줄 인자 → 검증 없이 → 시스템 설정 함수 인자 ⚠️ 외부에서는 “값”이 아니라 “선택지 번호”만 받게 하는 간접 참조(indirection)가 표준 해법이다.

공격 시나리오: 공격자가 명령줄 인자로 호스트 ID나 FTP 서비스 포트를 바꾸면 시스템이 다르게 식별되거나 기존 서비스와 충돌해 서비스가 중단된다.

❌ Bad

main(int argc, char *argv[])
{
    sethostid(atol(argv[1]));        /* 외부 입력으로 변경 */
}
const char *servicePort = GetParameter(queryStr, PORT_PARM);
ChangeServicePort(FTP_SERIVCE, servicePort);

✅ Good

main(int argc, char *argv[])
{
    sethostid(0xC0A80101);           /* 고정 상수 사용 */
}
if (servicePortIndex == DEFAULT)
    ChangeServicePort(FTP_SERVICE, DEFAULT_FTP_PORT);
else if (servicePortIndex == ALTERNATIVE)
    ChangeServicePort(FTP_SERVICE, ALTERNATIVE_FTP_PORT);

치환 규칙: 설정 함수 인자에 외부 값 직접 전달 금지 → 내부 상수 또는 “인덱스 → 상수” 매핑 테이블 경유.


10. 프로세스 제어 CWE-114

신뢰 경계: 환경변수(SHAREDFILE)·요청 파라미터 → 검증 없이 → 라이브러리 경로·OS 명령어 ⚠️ 실행 대상(라이브러리 경로, 명령어)은 코드에 고정하거나 화이트리스트 맵을 거치게 하라.

공격 시나리오: SHAREDFILE 환경변수를 악성 .so 로 바꾸면 해당 프로세스 권한으로 임의 코드가 실행된다.

❌ Bad

char *filename = getenv("SHAREDFILE");
handle = dlopen(filename, RTLD_LAZY);
 
string op = GetRequestParameter(queryStr, CONVERT_PARAM);
op = op + " " + documentName;
system(op);                          /* 외부 입력을 그대로 OS 명령으로 */

✅ Good

char *filename = "/usr/lib/hello.so";           /* 절대경로 */
handle = dlopen(filename, RTLD_LAZY);
 
additionalOperations.insert("DocToHtml", "convertDocToHtml");   /* 허용 명령 매핑 */
string osCommand = additionalOperations[op] + " " + documentName;
system(osCommand);                              /* 매핑을 통과한 값만 실행 */

치환 규칙: 라이브러리 경로는 상대경로·환경변수 대신 신뢰 디렉터리 절대경로. 실행 명령은 화이트리스트 맵 결과값만 사용.


이 계열 미니 체크리스트

  • 쿠키·히든필드·요청 파라미터 값으로 직접 권한/역할을 판단하는 코드가 있는가
  • sethostid() · dlopen() · 서비스 포트 변경처럼 시스템 설정 함수에 외부 입력이 그대로 들어가는가
  • 클라이언트가 “값”을 보내는가, 아니면 “선택지 인덱스”만 보내는가 — 값이라면 간접 참조로 바꿀 수 있는가
  • 권한 판단 근거가 서버 세션(내부 확정값)에 있는가, 요청 데이터(외부 입력)에 있는가

관련 노트